using System;
using System.Collections;
using System.Collections.Generic;
using QFramework.Framework.Util;
using UnityEngine;

namespace QFramework.Framework
{
    /**
     * MonoBehaviour 简化
     */
    public abstract class MonoBehaviourSimplify : MonoBehaviour
    {
        #region Delay

        public void Delay(float seconds, Action onFinished)
        {
            StartCoroutine(DelayCoroutine(seconds, onFinished));
        }

        private IEnumerator DelayCoroutine(float seconds, Action onFinished)
        {
            yield return new WaitForSeconds(seconds);
            onFinished();
        }

        #endregion

        #region MsgDispatcher

        private readonly List<MsgRecord> _msgRecorder = new();

        protected void RegisterMsg(string msgName, Action<object> onMsgReceived)
        {
            MsgDispatcher.Register(msgName, onMsgReceived);
            _msgRecorder.Add(MsgRecord.Allocate(msgName, onMsgReceived));
        }

        protected void UnRegisterMsg(string msgName)
        {
            var selectedRecords = _msgRecorder.FindAll(recorder => recorder.Name == msgName);
            selectedRecords.ForEach(selectedRecord =>
            {
                MsgDispatcher.UnRegister(selectedRecord.Name, selectedRecord.OnMsgReceived);
                _msgRecorder.Remove(selectedRecord);
                selectedRecord.Recycle();
            });
            selectedRecords.Clear();
        }

        protected void UnRegisterMsg(string msgName, Action<object> onMsgReceived)
        {
            var selectedRecords = _msgRecorder.FindAll(recorder =>
                recorder.Name == msgName && recorder.OnMsgReceived == onMsgReceived);

            selectedRecords.ForEach(selectedRecord =>
            {
                MsgDispatcher.UnRegister(selectedRecord.Name, selectedRecord.OnMsgReceived);
                _msgRecorder.Remove(selectedRecord);
                selectedRecord.Recycle();
            });

            selectedRecords.Clear();
        }

        protected void SendMsg(string msgName, object data)
        {
            MsgDispatcher.Send(msgName, data);
        }

        private void OnDestroy()
        {
            OnBeforeDestroy();

            foreach (var msgRecord in _msgRecorder)
            {
                MsgDispatcher.UnRegister(msgRecord.Name, msgRecord.OnMsgReceived);
                msgRecord.Recycle();
            }

            _msgRecorder.Clear();
        }

        protected abstract void OnBeforeDestroy();

        private class MsgRecord
        {
            private static readonly Stack<MsgRecord> MsgRecordPool = new();

            public string Name;
            public Action<object> OnMsgReceived;

            public static MsgRecord Allocate(string msgName, Action<object> onMsgReceived)
            {
                MsgRecord retMsgRecord = null;
                retMsgRecord = MsgRecordPool.Count > 0 ? MsgRecordPool.Pop() : new MsgRecord();

                retMsgRecord.Name = msgName;
                retMsgRecord.OnMsgReceived = onMsgReceived;

                return retMsgRecord;
            }

            public void Recycle()
            {
                Name = null;
                OnMsgReceived = null;

                MsgRecordPool.Push(this);
            }
        }

        #endregion
    }
}